#include "include/Ymodem.h"
#include <zephyr/drivers/watchdog.h>
/*********************************************************************
 * 需要的标志定义
 *********************************************************************/
typedef volatile uint32_t  vu32;
#define STM32_FLASH_BASE 0x08000000

#define YMODEM_POLY    0x1021

/*********************************************************************
 * 全局变量与外部函数和变量声明
 *********************************************************************/
uint8_t file_name[FILE_NAME_LENGTH];
uint16_t Cal_Crc16(unsigned char *ptr, uint16_t len,uint16_t poly) ;
static const struct device *flash_device =
		DEVICE_DT_GET_OR_NULL(DT_CHOSEN(zephyr_flash_controller));
extern uint32_t FLASH_BASE_ADDR;
/*******************************************************************************
  * @函数名称	Receive_Byte
  * @函数说明   从发送端接收一个字节
  * @输入参数   c: 接收字符
                timeout: 超时时间
  * @输出参数   无
  * @返回参数   接收的结果
                0：成功接收
                1：时间超时
*******************************************************************************/
static uint32_t Receive_Byte(uint8_t *c, uint32_t timeout)
{
    const struct device *dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
    while (timeout-- > 0)
    {
        if ( uart_poll_in(dev, c) == 0)
        {
            return 0;
        }
    }
    return -1;
}
 
/*******************************************************************************
  * @函数名称	Receive_Packet
  * @函数说明   从发送端接收一个数据包
  * @输入参数   data ：数据指针
                length：长度
                timeout ：超时时间
  * @输出参数   无
  * @返回参数   接收的结果
                0: 正常返回
                -1: 超时或者数据包错误
                1: 用户取消
*******************************************************************************/
static int32_t Receive_Packet(uint8_t *data, int32_t *length, uint32_t timeout)
{
    uint16_t i, packet_size;
    uint8_t c;
    *length = 0;
    if (Receive_Byte(&c, timeout) != 0)
    {
        return -1;
    }
    switch (c)
    {
    case SOH:
        packet_size = PACKET_SIZE;
        break;
    case STX:
        packet_size = PACKET_1K_SIZE;
        break;
    case EOT:
        return 0;
    case CA:
        if ((Receive_Byte(&c, timeout) == 0) && (c == CA))
        {
            *length = -1;
            return 0;
        }
        else
        {
            return -1;
        }
    case ABORT1:
    case ABORT2:
        return 1;
    default:
        return -1;
    }
    *data = c;
    for (i = 1; i < (packet_size + PACKET_OVERHEAD); i++)
    {
        if (Receive_Byte(data + i, timeout) != 0)
        {
            return -1;
        }
    }
    if (data[PACKET_SEQNO_INDEX] != ((data[PACKET_SEQNO_COMP_INDEX] ^ 0xff) & 0xff))
    {
        return -1;
    }
    *length = packet_size;
    return 0;
}
 
/*******************************************************************************
  * @函数名称	Ymodem_Receive
  * @函数说明   通过 ymodem协议接收一个文件
  * @输入参数   buf: 首地址指针
  * @输出参数   无
  * @返回参数   文件长度
*******************************************************************************/
int32_t Ymodem_Receive(uint8_t *buf,uint32_t addrDst)
{
    uint8_t packet_data[PACKET_1K_SIZE + PACKET_OVERHEAD], file_size[FILE_SIZE_LENGTH], *file_ptr, *buf_ptr;
    int32_t i, packet_length, session_done, file_done, packets_received, errors, session_begin, size = 0;
    const struct device *dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
    //初始化Flash地址变量
    uint32_t FlashDestination = addrDst;
  
    for (session_done = 0, errors = 0, session_begin = 0;;)
    {
        for (packets_received = 0, file_done = 0, buf_ptr = buf;;)
        {
            switch (Receive_Packet(packet_data, &packet_length, NAK_TIMEOUT))
            {
            case 0:
                errors = 0;
                switch (packet_length)
                {
                    //发送端终止
                case -1:
                    uart_poll_out(dev,ACK);
                    return 0;
                    //结束传输
                case 0:
                    uart_poll_out(dev,ACK);
                    file_done = 1;
                    break;
                    //正常的数据包
                default:
                    if ((packet_data[PACKET_SEQNO_INDEX] & 0xff) != (packets_received & 0xff))
                    {
                        uart_poll_out(dev,NAK);
                    }
                    else
                    {
                        if (packets_received == 0)
                        {
                            //文件名数据包
                            if (packet_data[PACKET_HEADER] != 0)
                            {
                                //文件名数据包有效数据区域
                                for (i = 0, file_ptr = packet_data + PACKET_HEADER; (*file_ptr != 0) && (i < FILE_NAME_LENGTH);)
                                {
                                    file_name[i++] = *file_ptr++;
                                }
                                file_name[i++] = '\0';
                                for (i = 0, file_ptr++; (*file_ptr != ' ') && (i < FILE_SIZE_LENGTH);)
                                {
                                    file_size[i++] = *file_ptr++;
                                }
                                file_size[i++] = '\0';
                                size = strtol(file_size,NULL ,10) & 0xffffffff;
 
                                //测试数据包是否过大
                                if (size > (APP_FLASH_SIZE - 1))
                                {
                                    //结束
                                    uart_poll_out(dev,CA);
                                    uart_poll_out(dev,CA);
                                    return -1;
                                }
                                
                                iap_erase_flash(FlashDestination, size);  //清空flash扇区
                                uart_poll_out(dev,ACK);
                                uart_poll_out(dev,CRC16);
                            }
                            //文件名数据包空，结束传输
                            else
                            {
                                uart_poll_out(dev,ACK);
                                file_done = 1;
                                session_done = 1;
                                break;
                            }
                        }
                        //数据包
                        else
                        {
                            memcpy(buf_ptr, packet_data + PACKET_HEADER, packet_length);
                            iap_write_flash(FlashDestination, buf_ptr, packet_length);  //写flash
                            if (*(uint32_t *)(FLASH_BASE_ADDR +FlashDestination) != *(uint32_t *)buf_ptr)  //错误
                            {
                                uart_poll_out(dev,CA);
                                uart_poll_out(dev,CA);
                                return -2;
                            }
                            FlashDestination += packet_length;
                            uint16_t crc = packet_data[PACKET_OVERHEAD + packet_length - 2] << 8 | packet_data[PACKET_OVERHEAD + packet_length - 1];
                            if(crc == Cal_Crc16(packet_data + PACKET_HEADER,packet_length,YMODEM_POLY)){
                                uart_poll_out(dev,ACK);
                            }
                            else{
                                uart_poll_out(dev,NAK);
                            }
                        }
                        packets_received++;
                        session_begin = 1;
                    }
                }
                break;
            case 1:
                uart_poll_out(dev,CA);
                uart_poll_out(dev,CA);
                return -3;
            default:
                if (session_begin > 0)
                {
                    errors++;
                }
                if (errors > MAX_ERRORS)
                {
                    uart_poll_out(dev,CA);
                    uart_poll_out(dev,CA);
                    return 0;
                }
                uart_poll_out(dev,CRC16);
                break;
            }
            if (file_done != 0)
            {
                break;
            }
        }
        if (session_done != 0)
        {
            break;
        }
    }
    return (int32_t)size;
}
 
/*******************************************************************************
  * @函数名称	SerialDownload
  * @函数说明   通过串口接收一个文件
  * @输入参数   烧录的片上flash块偏移地址:
  * @输出参数   无
  * @返回参数   无
*******************************************************************************/
uint8_t tab_1024[1024] ={0};

int32_t SerialDownload(uint32_t addrDst)
{
    int32_t Size = 0;
    
    printk("Waiting for the file(press 'a' to abort)\r\n");
    Size = Ymodem_Receive(&tab_1024[0],addrDst);
    if (Size > 0)
    {
        printk("\r\nProgramming Successfully!\r\nName: ");
        printk("%s",file_name);
        printk("\r\nSize: %d Bytes",Size);
    }
    else if (Size == -1)
    {
        printk("\r\nThe image size is higher than the allowed space memory!\r\n");
    }
    else if (Size == -2)
    {
        printk("\r\n\rVerification failed!\r\n");
    }
    else if (Size == -3)
    {
        printk("\r\nAborted by user.\r\n");
    }
    else
    {
        printk("\r\nFailed to receive the file!\r\n");
    }
    return Size;
}

/*******************************************************************************
  * @函数名称	Cal_Crc16
  * @函数说明   通用crc16校验
  * @输入参数   校验数据、数据长度、多项式值:
  * @输出参数   无
  * @返回参数   crc校验值
*******************************************************************************/
uint16_t Cal_Crc16(uint8_t *ptr, uint16_t len,uint16_t poly) 
{ 
  	uint16_t crc = 0;
  	uint16_t i,j;
  	for(i = 0;i < len ;i++)
  	{
		crc=crc^ptr[i]<<8;  		
		for(j=0;j<8;j++)
		{
			if((crc&((uint16_t)0x8000))!=0)
			{
				crc=((crc << 1)^poly);
			}
			else
			{
				crc=crc<<1;
			}					
		}
  	}
  	return crc&0xffff;
} 
 

/*******************************************************************************
  * @函数名称	iap_erase_flash  
  * @函数说明   清flash
  * @输入参数   烧录的片上flash块偏移地址和长度:
  * @输出参数   无
  * @返回参数   成功与否
*******************************************************************************/
uint32_t iap_erase_flash(uint32_t addrDst, uint32_t length)
{ 
    flash_erase(flash_device,addrDst,length);
    return 0;
}

/*******************************************************************************
  * @函数名称	iap_write_flash  
  * @函数说明   烧写flash
  * @输入参数   烧录的片上flash块基地址和长度:
  * @输出参数   无
  * @返回参数   成功与否
*******************************************************************************/
uint32_t iap_write_flash(uint32_t addrDst, uint8_t *addrSrc, uint32_t length)
{  
    flash_write(flash_device,addrDst,addrSrc,length);
    return 0;
}

